/*
 * This program is free software; you can redistribute it and/or modify 
 * it under the terms of the GNU General Public License as published by 
 * the Free Software Foundation; either version 3 of the License, or 
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful, but 
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 
 * for more details.
 * 
 * You should have received a copy of the GNU General Public License along 
 * with this program; if not, write to the Free Software Foundation, Inc., 
 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

#include <ctime>
#include <QMutexLocker>
#include "thread.h"
#include "post.h"
#include "board.h"
#include "threadstorage.h"
#include "log.h"
#include "text.h"

namespace Wishmaster
{

	Thread::Thread(const ThreadId& threadId, Board* parent) : QObject(parent),
		m_opPost(NULL),
		m_threadId(threadId),
		m_postcount(0),
		m_hidden(false),
		m_state(sNewThread),
		m_mutex(QMutex::Recursive),
		m_parent(parent),
		m_page(0)
	{
		m_visiblePosts = new EntityCollector<PostPtr, PostId>();
		m_flags = QBitArray(MaxThreadFlag);
		setFlag(Alive, true);
		setFlag(Swapped, false);
	}

	Thread::~Thread()
	{
		delete m_visiblePosts;
	}

	ThreadId Thread::id() const
	{
		return m_threadId;
	}

	void Thread::appendPost(const PostPtr& post)
	{
		visiblePosts()->push_back(post);
	}

	PostPtr Thread::getOpPost()
	{
		if(flag(Thread::Swapped))
			return PostPtr();
		return visiblePosts()->getNth(0);
	}

	int Thread::getPostsCount() const
	{
		if(flag(Thread::Swapped) || visiblePosts()->count() == 0)
		{
			return m_postcount;
		}
		else
		{
			m_postcount = visiblePosts()->count();
			return m_postcount;
		}
	}

	void Thread::setPostsCount(int count)
	{
		m_postcount = count;
	}

	PostPtr Thread::getPost(int number) const
	{
		if(flag(Thread::Swapped))
			return PostPtr();
		return visiblePosts()->getNth(number);
	}

	PostPtr Thread::getPost(int number)
	{
		QMutexLocker locker(&m_mutex);
		if(flag(Swapped))
			doUnswap();
		return visiblePosts()->getNth(number);
	}

	int Thread::getPostPosition(const PostId& id)
	{
		QMutexLocker locker(&m_mutex);
		if(flag(Thread::Swapped))
			doUnswap();
		return visiblePosts()->getPosition(id);
	}

	int Thread::getActualPostsCount() const
	{
		return visiblePosts()->count();
	}

	void Thread::clear()
	{
		visiblePosts()->clear();
	}

	void Thread::setTimestamp(const QDateTime& ts)
	{
		m_accessTimestamp = ts;
	}

	QDateTime Thread::getTimestamp() const
	{
		return m_accessTimestamp;
	}

	void Thread::setThreadTimestamp(const QDateTime& ts)
	{
		m_threadTimestamp = ts;
	}

	QDateTime Thread::threadTimestamp() const
	{
		return m_threadTimestamp;
	}

	void Thread::setName(const QString& name)
	{
		m_name = name;
	}

	QString Thread::getShortName()
	{
		QMutexLocker locker(&m_mutex);
		if(flag(Swapped))
			doUnswap();
		if(!getOpPost().isNull())
		{
			if(!getOpPost()->topic().trimmed().isEmpty())
				return getOpPost()->topic();
		}
		return id().threadStringId();
	}

	QString Thread::getName()
	{
		if(flag(Swapped))
		{
			return m_name;
		}
		if(!getOpPost().isNull())
		{
			if(!getOpPost()->topic().trimmed().isEmpty())
			{
				m_name = getOpPost()->topic();
				return getOpPost()->topic();
			}
			else if(!getOpPost()->text().trimmed().isEmpty())
			{
				m_name = htmlToPlainText(getOpPost()->text());
				return m_name;
			}
		}
		else if(!m_name.isEmpty())
			return m_name;
		return id().threadStringId();
	}

	PostId Thread::getPostByLink(const QString& link)
	{
		Board* b = static_cast<Board*>(this->parent());
		return b->getPostID(link, m_threadId);
	}

	void Thread::setSortOrder(int order)
	{
		m_sortOrder = order;
	}
	
	int Thread::getSortOrder() const
	{
		return m_sortOrder;
	}

	void Thread::doSwap()
	{
		ThreadStorage* storage = m_parent->threadStorage();
		if(!flag(Thread::Swapped))
		{
			log(llDebug, "Thread::swap(%d)", m_threadId.threadNumericId());
			storage->storeThread(m_parent->getParent()->getHost(), m_parent->name(), this);
			visiblePosts()->clear();
			setFlag(Thread::Swapped, true);
			log(llDebug, "done");
		}
	}

	void Thread::doUnswap()
	{
		ThreadStorage* storage = m_parent->threadStorage();
		if(flag(Thread::Swapped))
		{
			log(llDebug, "Thread::unswap(%d)", m_threadId.threadNumericId());
			if(storage->restoreThread(m_parent, m_threadId.threadNumericId(), this))
				setFlag(Thread::Swapped, false);
		}
	}

	void Thread::swap()
	{
		QMutexLocker locker(&m_mutex);
		doSwap();
	}

	void Thread::unswap()
	{
		QMutexLocker locker(&m_mutex);
		doUnswap();
	}

	bool Thread::flag(ThreadFlag fl) const
	{
		return m_flags.testBit(fl);
	}

	void Thread::setFlag(ThreadFlag fl, bool value)
	{
		m_flags.setBit(fl, value);
	}

	ComponentVisibility Thread::visibility() const
	{
		return m_visibility;
	}

	void Thread::setVisibility(ComponentVisibility newVisibility)
	{
		m_visibility = newVisibility;
	}

	Thread::State Thread::state()
	{
		return m_state;
	}

	void Thread::setState(Thread::State s)
	{
		m_state = s;
	}

	void Thread::hidePost(const PostId& id)
	{
		QMutexLocker locker(&m_mutex);
		if(flag(Swapped))
			doUnswap();
		PostPtr post = visiblePosts()->lookup(id);
		post->setVisibility(Hide);
	}

	bool Thread::isPostHidden(const PostId& id)
	{
		QMutexLocker locker(&m_mutex);
		if(flag(Swapped))
			doUnswap();
		PostPtr p = visiblePosts()->lookup(id);
		if(!p)
			return false;
		return ((p->visibility() == Hide) ||
				(p->visibility() == HideForever));
	}

	PostPtr Thread::pickPost(const PostId& id)
	{
		QMutexLocker locker(&m_mutex);
		return visiblePosts()->pick(id);
	}

	PostPtr Thread::lookupPost(const PostId& id)
	{
		PostPtr p = visiblePosts()->lookup(id);
		return p;
	}

	PostId Thread::lastPostId() const
	{
		if(visiblePosts()->count() == 0)
			return PostId();
		return visiblePosts()->getNth(visiblePosts()->count() - 1)->id();
	}

	EntityCollector<PostPtr, PostId>* Thread::visiblePosts() const
	{
		return m_visiblePosts;
	}

	QUrl Thread::leaderPicUrl() const
	{
		return m_leaderPicUrl;
	}

	void Thread::setLeaderPicUrl(const QUrl& url)
	{
		m_leaderPicUrl = url;
	}

	QImage Thread::leaderPic() const
	{
		return m_leaderPic;
	}

	void Thread::setLeaderPic(const QImage& image)
	{
		m_leaderPic = image;
	}

	int Thread::page() const
	{
		return m_page;
	}

	void Thread::setPage(int page)
	{
		m_page = page;
	}
}
